package day15;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

//用户系统
public class AccountSystem {
    static Scanner sc = new Scanner(System.in);
    static List<Account> list = new ArrayList<>();
    static ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);

    public static void main(String[] args) {
        one();
    }

    public static void one() {
        while (true) {
            System.out.println("********XXX系统********");
            System.out.println("1.登录");
            System.out.println("2.注册");
            System.out.println("3.退出");
            System.out.println("*****请输入编号*****");
            int n = sc.nextInt();
            switch (n) {
                case 1:
                    login();
                    break;
                case 2:
                    reg();
                    break;
                case 3:
                    return;
            }
        }
    }

    //如果第一次登录成功，可以记录缓存(登录成功的记录)
    //如果下一次再登录，还是相同的账号(可以不输入密码，直接免登录)
    //缓存的记录 一直存在30秒失效
    public static void login() {
        System.out.println("请输入你的账号");
        String account = sc.next();
        //通过缓存验证是否登录过 ==>two()
        Account acc = checkCookie(account);
        if (acc != null) {
            two(acc);
            return;
        }
        System.out.println("请输入你的密码");
        String password = sc.next();
        for (Account a : list) {
            if (account.equals(a.username) && password.equals(a.password)) {
                System.out.println("是否保存本地缓存y/n,30秒内免登录");
                String yn = sc.next();
                if ("y".equals(yn)) {
                    addCookie(a);
                    //场景可周期定长池  30S首次延迟功能

                    ses.schedule(() -> {
                        removeCookie(account);
                    }, 30, TimeUnit.SECONDS);
                }
                two(a);
                return;
            }
        }
    }

    public static void removeCookie(String account) {
        File f = new File("d://io//cookie-" + account + ".txt");
        if (f.delete()) System.out.println("缓存失效了");
    }

    public static Account checkCookie(String account) {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d://io//cookie-" + account + ".txt")))) {
            String msg = br.readLine();
            //将账号加密后和之前保存加密信息 匹配是否一致
            if (msg != null && msg.equals(md5(account))) {
                for (Account a : list) {
                    if (a.username.equals(account)) return a;
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("首次登录");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String md5(String msg) {
        StringBuilder hexString = null;
        try {
            // 获取 MessageDigest 类的实例，指定算法为 MD5
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 对字符串进行加密
            byte[] encodedHash = digest.digest(msg.getBytes());
            // 将加密后的字节数组转换为十六进制字符串
            hexString = new StringBuilder();
            for (byte b : encodedHash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return hexString.toString();
    }

    public static void addCookie(Account a) {   //cookie-aaa.txt
        try (FileOutputStream fos = new FileOutputStream("d://io//cookie-" + a.username + ".txt")) {
            fos.write(md5(a.username).getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void reg() {
        System.out.println("请输入你的账号");
        String account = sc.next();
        System.out.println("请输入你的密码");
        String password = sc.next();
        Account a = new Account(account, password);
        list.add(a);
        //存储集合
        //做序列化
    }

    //登录成功会进入二级菜单，连接socket服务端
    public static void two(Account a) {
        System.out.println(a.username + "登录成功");
        Socket client = null;
        try {
            client = new Socket("127.0.0.1", 9999);
        } catch (Exception e) {
        }
        //存储客户端对象
        AccountServer.map.put(a.username, client);

        //读取服务端给我写的数据(怎么读不会影响主程序  所以是客户端线程)
        ClientThread t = new ClientThread(client);
        while (true) {
            System.out.println("1.聊天功能");//进行私聊
            System.out.println("2.退出登录");
            System.out.println("0.返回上级");
            int n = sc.nextInt();
            switch (n) {
                case 1:
                    chat(a, client);
                    break;
                case 2:
                    removeCookie(a.username);
                    //客户端线程 要停  删除socket对象
                    t.b=true;
                    AccountServer.map.remove(a.username);
                    break;
                case 0:
                    return;
            }
        }
    }

    //还要告诉服务端 发送方 和接收方
    //进行私聊：创建输出流 去写数据(手动输入)给 发送方
    public static void chat(Account a, Socket client) {
        System.out.println("请输入对方账号");
        String to = sc.next();
        try {
            OutputStream os = client.getOutputStream();
            //第一次给服务端写入时：告诉它发送方和接收方
            os.write((a.username + "=>" + to + "\n").getBytes());
            //开始聊天
            while (true) {
                System.out.println("请输入你要向：" + to + "发送的消息，esc退出");
                String msg = sc.nextLine();
                //退出聊天(只是不想发送消息了，但是账号是没有退出登录的，不影响接收)
                if ("esc".equals(msg)) {
                    os.write((a.username + " 说：esc\n").getBytes());
                    return;
                }
                os.write((a.username + " 说：" + msg + "\n").getBytes());
            }
        } catch (IOException     e) {
            e.printStackTrace();
        }
    }
}

class Account {
    String username;
    String password;

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

//Socket服务端：是和主程序 独立 而且是先开好的
class AccountServer {
    //key：账号  value：对应的客户端对象
    static Map<String, Socket> map = new HashMap<>();

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9999);
        System.out.println("启动服务端成功");
        while (true) {
            Socket client = server.accept();
            //线程
            new ServerThread(client).start();
        }
    }
}

class ServerThread extends Thread {
    Socket client;

    public ServerThread(Socket client) {
        this.client = client;
    }

    //先接收数据(得到发送方 和 接收方 的client对象)
    //发送方 进行读取    对接收方进行写入
    //如果对方不在线   可以实现离线消息存储，也可以不发送消息
    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()))) {
            String fromAndTo = br.readLine();//aaa(发) => bbb(收)
            //fromTo[0] = 发送方  fromTo[1] = 接收方
            String[] fromTo = fromAndTo.split("=>");
            while (true) {
                //开始读取数据
                String msg = br.readLine();
                if (msg.contains("esc")){//是否要退出 目的是要结束线程 我不需要给别人发了
                    return;
                }
                if (AccountServer.map.containsKey(fromTo[1])) {//在线
                    //给接收方直接写入数据
                    Socket toClient = AccountServer.map.get(fromTo[1]);
                    OutputStream os = toClient.getOutputStream();
                    os.write((msg+"\n").getBytes());
                } else {//对方不在线
                    //给发送方返回一个结果
                    OutputStream os = client.getOutputStream();
                    os.write((fromTo[1] + "目前不在线\n").getBytes());
                }
            }
        } catch (Exception e) {

        }
    }
}
//客户端线程
class ClientThread extends Thread {
    Socket client;
    volatile boolean b;

    public ClientThread(Socket client) {
        this.client = client;
    }

    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()))) {
            while (!b) {
                String msg = br.readLine();
                if (msg != null && !msg.equals("")) {
                    System.out.println(msg);
                }
                //什么结束线程

            }
        } catch (Exception e) {

        }
    }
}